import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

public class OperatorRegistry {
    
    public static final int NOTATYPE = 0;
    public static final int INT = 1;
    public static final int STRING = 2;
    public static final int BOOL = 3;
    public static final int VOID = 4;
    public static final int INT_ARRAY = 5;
    public static final int STR_ARRAY = 6;
    public static final int BOOL_ARRAY = 7;
    public static final int VOID_ARRAY = 8;
    
    public static final int ADD = 1;
    public static final int SUB = 2;
    public static final int MUL = 3;
    public static final int DIV = 4;
    public static final int MOD = 5;
    public static final int LAND = 6;
    public static final int LOR = 7;
    public static final int LNOT = 8;
    public static final int GT = 9;
    public static final int GTE = 10;
    public static final int LT = 11;
    public static final int LTE = 12;
    public static final int EQ = 13;
    public static final int NEQ = 14;
    public static final int UNEG = 15;
    public static final int UPOS = 16;
    public static final int BAND = 17;
    public static final int BOR = 18;
    public static final int BNOT = 19;
    public static final int BXOR = 20;
    public static final int EXP = 21;
    public static final int INDX = 22;
    public static final int RNG = 23;
    
    private static Map<Integer, List<Integer>> supportedTypes;
    private static List<Integer> types;
    public static List<Integer> arrayTypes;
    
    static {
        supportedTypes = new HashMap<Integer, List<Integer>>();
        
        arrayTypes = new ArrayList<Integer>();
        arrayTypes.add( INT_ARRAY );
        arrayTypes.add( STR_ARRAY );
        arrayTypes.add( BOOL_ARRAY );
        arrayTypes.add( VOID_ARRAY );
        
        types = new ArrayList<Integer>();
        types.add( INT );
        types.add( BOOL );
        types.add( STRING );
        types.add( VOID );
        types.addAll( arrayTypes );
        
        List<Integer> ints = new ArrayList<Integer>();
        ints.add( INT );
        List<Integer> bools = new ArrayList<Integer>();
        bools.add( BOOL );
        List<Integer> strings = new ArrayList<Integer>();
        strings.add( STRING );
        List<Integer> intsAndStrings = new ArrayList<Integer>();
        intsAndStrings.add( INT );
        intsAndStrings.add( STRING );
        List<Integer> stringsAndArrays = new ArrayList<Integer>();
        stringsAndArrays.add( STRING );
        stringsAndArrays.addAll( arrayTypes );
        List<Integer> intsAndStringsAndArrays = new ArrayList<Integer>();
        intsAndStringsAndArrays.add(INT);
        intsAndStringsAndArrays.add(STRING);
        intsAndStringsAndArrays.addAll(arrayTypes);
        
        supportedTypes.put( ADD, intsAndStringsAndArrays );
        supportedTypes.put( SUB, ints );
        supportedTypes.put( MUL, intsAndStrings );
        supportedTypes.put( DIV, ints );
        supportedTypes.put( MOD, ints );
        supportedTypes.put( LAND, bools );
        supportedTypes.put( LOR, bools );
        supportedTypes.put( LNOT, bools );
        supportedTypes.put( GT, ints );
        supportedTypes.put( GTE, ints );
        supportedTypes.put( LT, ints );
        supportedTypes.put( LTE, ints );
        supportedTypes.put( EQ, intsAndStrings );
        supportedTypes.put( NEQ, intsAndStrings );
        supportedTypes.put( UNEG, ints );
        supportedTypes.put( UPOS, ints );
        supportedTypes.put( BAND, ints );
        supportedTypes.put( BOR, ints );
        supportedTypes.put( BNOT, ints );
        supportedTypes.put( BXOR, ints );
        supportedTypes.put( EXP, ints );
        supportedTypes.put( INDX, stringsAndArrays );
        supportedTypes.put( RNG, ints );
    }
    
    public static boolean isSupported( Integer type, Integer op ) {
        return ( supportedTypes.get( op ).contains( type ) );
    }
    
    public static boolean isValidType(int type) {
        return types.contains( type );
    }
    
    public static boolean isArrayType( int type ) {
        return arrayTypes.contains( type );
    }
    
    public static int toArrayType( int type ) {
        switch (type) {
            case INT:
                return INT_ARRAY;
            case STRING:
                return STR_ARRAY;
            case BOOL:
                return BOOL_ARRAY;
            default:
                return VOID_ARRAY;
        }     
    }
    
    public static int toNonArrayType( int type ) {
        switch( type ) {
            case INT_ARRAY:
                return INT;
            case STR_ARRAY:
                return STRING;
            case BOOL_ARRAY:
                return BOOL;
            default:
                return VOID;
        }
    }
    
    public static String typeToString( int type ) {
        String typeString;
        switch (type) {
            case INT:
                typeString = "int";
                break;
            case STRING:
                typeString = "string";
                break;
            case BOOL:
                typeString = "bool";
                break;
            case INT_ARRAY:
                typeString = "int[]";
                break;
            case STR_ARRAY:
                typeString = "string[]";
                break;
            case BOOL_ARRAY:
                typeString = "bool[]";
                break;
            case VOID_ARRAY:
                typeString = "array";
                break;
            default:
                typeString = "Unknown type";
                break;
        }
        return typeString;
    }
    
    public static String opToString( int op ) {
        String opString;
        switch (op) {
            case ADD:
                opString = "+";
                break;
            case SUB:
                opString = "-";
                break;
            case MUL:
                opString = "*";
                break;
            case DIV:
                opString = "/";
                break;
            case MOD:
                opString = "%";
                break;
            case LAND:
                opString = "and";
                break;
            case LOR:
                opString = "or";
                break;
            case LNOT:
                opString = "not";
                break;
            case GT:
                opString = ">";
                break;
            case GTE:
                opString = ">=";
                break;
            case LT:
                opString = "<";
                break;
            case LTE:
                opString = "<=";
                break;
            case EQ:
                opString = "==";
                break;
            case NEQ:
                opString = "!=";
                break;
            case UNEG:
                opString = "-";
                break;
            case UPOS:
                opString = "+";
                break;
            case BAND:
                opString = "&";
                break;
            case BOR:
                opString = "|";
                break;
            case BNOT:
                opString = "~";
                break;
            case BXOR:
                opString = "^";
                break;
            case EXP:
                opString = "**";
                break;
            case INDX:
                opString = "[]";
                break;
            case RNG:
                opString = ":";
                break;
            default:
                opString = "Unknown op";
                break;
        }
        return opString;
    }
    
}
